#include "mini.kit/safepack.h"
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>

namespace MINIKIT
{

SafePackException::SafePackException( const std::string& msg ):
m_message(msg)
{}

SafePackException::SafePackException( const char* fmt, ... )
{
	va_list args;
	va_start( args, fmt);
		MINIKIT::Text::sprintf( this->m_message, fmt, args );
	va_end( args );
}

SafePackException::~SafePackException() throw()
{
}

const char* SafePackException::what() const throw() 
{
	if (m_message.empty()) 
		return "Default Exception.";
	else
		return m_message.c_str();
}

SafePack::SafePack()
{
	this->detach();
}

void SafePack::attach(char* data_buf, uint32_t data_len, int8_t net_endian)
{
	this->m_net_endian = net_endian;
	this->m_data_buf = data_buf;
	this->m_data_len = this->m_left_len = data_len;
}

void SafePack::detach()
{
	this->m_net_endian = 0;
	this->m_data_buf = NULL;
	this->m_data_len = this->m_left_len = 0;
}

uint32_t SafePack::getProcessLength()
{ 
	return (this->m_data_len-this->m_left_len);
}

uint32_t SafePack::getRemainLength()
{
	return this->m_left_len;
}


uint64_t SafePack::ntohl64(uint64_t val)
{
	uint64_t res64;

#if(__BYTE_ORDER==__LITTLE_ENDIAN)
	uint32_t low = (uint32_t) (val & 0x00000000FFFFFFFFLL);
	uint32_t high = (uint32_t) ((val & 0xFFFFFFFF00000000LL) >> 32);
	low   = ::ntohl(low);
	high  = ::ntohl(high);
	res64 = (uint64_t) high + (((uint64_t) low) << 32);
#else
	res64 = val;
#endif/*__BYTE_ORDER==__LITTLE_ENDIAN*/
	return res64;
}

uint64_t SafePack::htonl64(uint64_t val)
{
	uint64_t res64;

#if(__BYTE_ORDER==__LITTLE_ENDIAN)
	uint32_t low = (uint32_t) (val & 0x00000000FFFFFFFFLL);
	uint32_t high = (uint32_t) ((val & 0xFFFFFFFF00000000LL) >> 32);
	low   = ::htonl(low);
	high  = ::htonl(high);
	res64 = (uint64_t) high + (((uint64_t) low) << 32);
#else
	res64 = val;
#endif/*__BYTE_ORDER==__LITTLE_ENDIAN*/
	return res64;
}


SafePackGetter::SafePackGetter() : SafePack()
{}

void SafePackGetter::get(int8_t  &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(int8_t) )
		throw SafePackException("Failed to get %s:int8_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	value = *(int8_t*)p;
	this->m_left_len -= sizeof(int8_t);
}

void SafePackGetter::get(uint8_t  &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint8_t) )
		throw SafePackException("Failed to get %s:uint8_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	value = *(uint8_t*)p;
	this->m_left_len -= sizeof(uint8_t);
}

void SafePackGetter::get(int16_t &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(int16_t) )
		throw SafePackException("Failed to get %s:int16_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		value = ::ntohs( *(int16_t*)p );
	else
		value = *(int16_t*)p;
	this->m_left_len -= sizeof(int16_t);
}

void SafePackGetter::get(uint16_t &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint16_t) )
		throw SafePackException("Failed to get %s:uint16_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		value = ::ntohs( *(uint16_t*)p );
	else
		value = *(uint16_t*)p;
	this->m_left_len -= sizeof(uint16_t);
}

void SafePackGetter::get(int32_t &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(int32_t) )
		throw SafePackException("Failed to get %s:int32_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		value = ::ntohl( *(int32_t*)p );
	else
		value = *(int32_t*)p;
	this->m_left_len -= sizeof(int32_t);
}

void SafePackGetter::get(uint32_t &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint32_t) )
		throw SafePackException("Failed to get %s:uint32_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		value = ::ntohl( *(uint32_t*)p );
	else
		value = *(uint32_t*)p;
	this->m_left_len -= sizeof(uint32_t);
}

void SafePackGetter::get(int64_t &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(int64_t) )
		throw SafePackException("Failed to get %s:int64_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
	{
		value = SafePack::ntohl64( *(int64_t*)p );
		//value = ntohl( *(int64_t*)p );
	}
	else
		value = *(int64_t*)p;
	this->m_left_len -= sizeof(int64_t);
}

void SafePackGetter::get(uint64_t &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint64_t) )
		throw SafePackException("Failed to get %s:uint64_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
	{
		
		value = SafePack::ntohl64( *(uint64_t*)p );
		//value = ntohl( *(uint64_t*)p );
	}
	else
		value = *(uint64_t*)p;
	this->m_left_len -= sizeof(uint64_t);
}

void SafePackGetter::get(std::string &value, const std::string &val_comment)
{
	uint32_t len = 0;
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint32_t) )
		throw SafePackException("Failed to get %s:string.uint32_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		len = ::ntohl( *(uint32_t*)p );
	else
		len = *(uint32_t*)p;
	this->m_left_len -= sizeof(uint32_t);
	
	if ( this->m_left_len < len )
		throw SafePackException("Failed to get %s:string.char*, when %d byte(s) left, expecting: %d byte(s).", val_comment.c_str(), this->m_left_len, len);
/*
	char buf[len+1];
	memcpy(buf, (p+sizeof(uint32_t)), len);
	buf[len] = 0x00;
	value = buf;
*/
	value.clear();
	value.resize( len );
	memcpy( (char*)value.c_str(), (p+sizeof(uint32_t)), len );
	this->m_left_len -= value.size();

}



SafePackSetter::SafePackSetter() : SafePack()
{}

void SafePackSetter::set(int8_t value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(int8_t) )
		throw SafePackException("Failed to set %s:int8_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	*(int8_t*)p = value;
	this->m_left_len -= sizeof(int8_t);
}

void SafePackSetter::set(uint8_t value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint8_t) )
		throw SafePackException("Failed to set %s:uint8_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	*(uint8_t*)p = value;
	this->m_left_len -= sizeof(uint8_t);
}

void SafePackSetter::set(int16_t value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(int16_t) )
		throw SafePackException("Failed to set %s:int16_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		*(int16_t*)p = ::htons( value );
	else
		*(int16_t*)p = value;
	this->m_left_len -= sizeof(int16_t);
}

void SafePackSetter::set(uint16_t value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint16_t) )
		throw SafePackException("Failed to set %s:uint16_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		*(uint16_t*)p = ::htons( value );
	else
		*(uint16_t*)p = value;
	this->m_left_len -= sizeof(uint16_t);
}

void SafePackSetter::set(int32_t value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(int32_t) )
		throw SafePackException("Failed to set %s:int32_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		*(int32_t*)p = ::htonl( value );
	else
		*(int32_t*)p = value;
	this->m_left_len -= sizeof(int32_t);
}

void SafePackSetter::set(uint32_t value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint32_t) )
		throw SafePackException("Failed to set %s:uint32_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian) 
		*(uint32_t*)p = ::htonl( value );
	else
		*(uint32_t*)p = value;
	this->m_left_len -= sizeof(uint32_t);
}

void SafePackSetter::set(int64_t value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(int64_t) )
		throw SafePackException("Failed to set %s:int64_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian)
	{
		*(int64_t*)p = SafePack::htonl64( value );
		//*(int64_t*)p = htonl( value );
	}
	else
		*(int64_t*)p = value;
	this->m_left_len -= sizeof(int64_t);
}

void SafePackSetter::set(uint64_t value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < sizeof(uint64_t) )
		throw SafePackException("Failed to set %s:uint64_t, when %d byte(s) left.", val_comment.c_str(), this->m_left_len);
	if(this->m_net_endian)
	{
		*(uint64_t*)p = SafePack::htonl64( value );
		//*(uint64_t*)p = htonl( value );
	}
	else
		*(uint64_t*)p = value;
	this->m_left_len -= sizeof(uint64_t);
}

void SafePackSetter::set(const std::string &value, const std::string &val_comment)
{
	char* p = this->m_data_buf + this->getProcessLength();
	if ( this->m_left_len < (sizeof(uint32_t)+value.size()) )
		throw SafePackException("Failed to set %s:string(uint32_t+char*), when %d byte(s) left, expecting: %d.", val_comment.c_str(), this->m_left_len, (sizeof(uint32_t)+value.size()) );
	
	if(this->m_net_endian) 
		*(uint32_t*)p = ::htonl( value.size() );
	else
		*(uint32_t*)p = value.size();
	memcpy((p+sizeof(uint32_t)), value.c_str(), value.size());
	this->m_left_len -= (sizeof(uint32_t)+value.size());
}

}; //namespace MINIKIT

